﻿using System;
using System.IO;
using System.Linq;
using System.Reflection;
using Abp.AspNetCore;
using Abp.AspNetCore.SignalR.Hubs;
using Abp.AspNetZeroCore.Web.Authentication.JwtBearer;
using Abp.Castle.Logging.Log4Net;
using Abp.Dependency;
using Abp.Extensions;
using Abp.PlugIns;
using Castle.Facilities.Logging;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Cors.Internal;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using ARchGL.Platform.Configuration;
using ARchGL.Platform.EntityFrameworkCore;
using ARchGL.Platform.Identity;
using ARchGL.Platform.Web.Chat.SignalR;
using PaulMiami.AspNetCore.Mvc.Recaptcha;
using Swashbuckle.AspNetCore.Swagger;
using ARchGL.Platform.Web.IdentityServer;
using ILoggerFactory = Microsoft.Extensions.Logging.ILoggerFactory;
using Abp.EntityFramework;
using Microsoft.Extensions.DependencyInjection.Extensions;
using ARchGL.Platform.Utils;
using Microsoft.AspNetCore.Http;
using Swashbuckle.AspNetCore.SwaggerGen;
using ARchGL.Platform.Web.Validation;
using Hangfire;
using Abp.Hangfire;
using ARchGL.Platform.Authorization;
using Hangfire.PostgreSql;
using ARchGL.Platform.Qianbian;
using ARchGL.Platform.FileServer;
using ARchGL.Platform.ThirdParty.Device;
using ARchGL.Platform.Mpp;

namespace ARchGL.Platform.Web.Startup
{
    public class Startup
    {
        private const string DefaultCorsPolicyName = "localhost";

        private readonly IConfigurationRoot appConfiguration;
        private readonly IHostingEnvironment _hostingEnvironment;

        public Startup(IHostingEnvironment env)
        {
            _hostingEnvironment = env;
            appConfiguration = env.GetAppConfiguration();
        }

        public IServiceProvider ConfigureServices(IServiceCollection services)
        {
            services.AddSingleton<IDbContextEntityFinder, PluginEfCoreDbContextEntityFinder>();
            services.TryAddSingleton<IHttpContextAccessor, HttpContextAccessor>();
            services.ConfigureOption(appConfiguration.GetSection("Authentication:SpiderConfigOptions"), () => new SpiderConfigOptions());

            //MVC
            services.AddMvc(options =>
            {
                options.Filters.Add(new CorsAuthorizationFilterFactory(DefaultCorsPolicyName));

                //注：ABP解释针对ApplicationService进行拦截
                //options.Filters.Add(typeof(JN_ValidationInterceptor));

                //注：ABP解释针对Controller进行拦截
                //options.Filters.Add(typeof(JN_ValidationFilter));

            }).SetCompatibilityVersion(CompatibilityVersion.Version_2_1); ;

            services.AddSignalR(options => { options.EnableDetailedErrors = true; });

            //Configure CORS for angular2 UI
            services.AddCors(options =>
            {
                options.AddPolicy(DefaultCorsPolicyName, builder =>
                {
                    //App:CorsOrigins in appsettings.json can contain more than one address with splitted by comma.
                    builder
                        .WithOrigins(
                            // App:CorsOrigins in appsettings.json can contain more than one address separated by comma.
                            appConfiguration["App:CorsOrigins"]
                                .Split(",", StringSplitOptions.RemoveEmptyEntries)
                                .Select(o => o.RemovePostFix("/"))
                                .ToArray()
                        )
                        .SetIsOriginAllowedToAllowWildcardSubdomains()
                        .AllowAnyHeader()
                        .AllowAnyMethod()
                        .AllowCredentials();
                });
            });

            FileServerHttpsModule.Register(services, appConfiguration); //注册文件服务
            QianbianHttpsModule.Register(services, appConfiguration); //注册千变 WebClient 信息
            ThirdPartyTowerCraneAndHoistHttpRegistrar.Register(services, appConfiguration); //第三方服务（起重机械、实名制） WebClient 信息
            ThirdPartyVideoHttpRegistrar.Register(services, appConfiguration); //第三方服务（萤石视频） WebClient 信息
            ThirdPartyProjectUserHttpRegistrar.Register(services, appConfiguration); //第三方服务（建委考勤） WebClient 信息
            ThirdPartyProjectUserRegistHttpRegistrar.Register(services, appConfiguration); //第三方服务（建委注册） WebClient 信息
            MppHttpsModule.Register(services, appConfiguration); //注册SpiderBim文件服务 WebClient 信息
            IdentityRegistrar.Register(services);
            AuthConfigurer.Configure(services, appConfiguration);

            //Identity server
            if (bool.Parse(appConfiguration["IdentityServer:IsEnabled"]))
            {
                IdentityServerRegistrar.Register(services, appConfiguration);
            }

            //Swagger - Enable this line and the related lines in Configure method to enable swagger UI
            services.AddSwaggerGen(options =>
            {
                options.SwaggerDoc("v1", new Info { Title = appConfiguration["App:Title"], Version = "v1" });
                options.DocInclusionPredicate((docName, description) => true);

                IncludeXmlComments(options);

                //Note: This is just for showing Authorize button on the UI. 
                //Authorize button's behaviour is handled in wwwroot/swagger/ui/index.html
                options.AddSecurityDefinition("Bearer", new BasicAuthScheme());

                // Assign scope requirements to operations based on AuthorizeAttribute
                options.OperationFilter<SecurityRequirementsOperationFilter>();
            });

            //Recaptcha
            services.AddRecaptcha(new RecaptchaOptions
            {
                SiteKey = appConfiguration["Recaptcha:SiteKey"],
                SecretKey = appConfiguration["Recaptcha:SecretKey"]
            });

            if (bool.Parse(appConfiguration["Abp:HangfireIsEnabled"]))
            {
                //Hangfire (Enable to use Hangfire instead of default job manager)
                services.AddHangfire(config =>
                {
                    config.UsePostgreSqlStorage(appConfiguration.GetConnectionString("Default"));
                });
            }

            //Configure Abp and Dependency Injection
            return services.AddAbp<PlatformWebHostModule>(options =>
            {
                //Configure Log4Net logging
                options.IocManager.IocContainer.AddFacility<LoggingFacility>(
                    f => f.UseAbpLog4Net().WithConfig("log4net.config")
                );

                options.PlugInSources.AddFolder(Path.Combine(_hostingEnvironment.WebRootPath, "Plugins"), SearchOption.AllDirectories);
            });
        }

        private void IncludeXmlComments(SwaggerGenOptions options)
        {
            var baseDirectory = Path.GetDirectoryName(typeof(Program).Assembly.Location);
            var di = new DirectoryInfo(baseDirectory);
            foreach (var item in di.GetFiles("*.xml"))
            {
                options.IncludeXmlComments(item.FullName);
            }
        }

        public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
        {
            //Initializes ABP framework.
            app.UseAbp(options =>
            {
                options.UseAbpRequestLocalization = false; //used below: UseAbpRequestLocalization
            });

            //注册404
            //app.UseErrorStateCodeRegister();
            //app.UseStatusCodePagesWithReExecute("/Error/Error404");

            app.UseCors(DefaultCorsPolicyName); //Enable CORS!

            app.UseAuthentication();
            app.UseJwtTokenMiddleware();

            if (bool.Parse(appConfiguration["IdentityServer:IsEnabled"]))
            {
                app.UseJwtTokenMiddleware("IdentityBearer");
                app.UseIdentityServer();
            }

            app.UseStaticFiles();

            using (var scope = app.ApplicationServices.CreateScope())
            {
                if (scope.ServiceProvider.GetService<HostDatabaseCheckHelper>().Exist(appConfiguration["ConnectionStrings:Default"]))
                {
                    app.UseAbpRequestLocalization();
                }
            }

            app.UseSignalR(routes =>
            {
                routes.MapHub<AbpCommonHub>("/signalr");
                routes.MapHub<ChatHub>("/signalr-chat");
            });

            if (bool.Parse(appConfiguration["Abp:HangfireIsEnabled"]))
            {
                //Hangfire dashboard &server(Enable to use Hangfire instead of default job manager)
                app.UseHangfireDashboard("/hangfire", new DashboardOptions
                {
#if !DEBUG
                Authorization = new[] { new AbpHangfireAuthorizationFilter(AppPermissions.Pages_Administration_HangfireDashboard) }
#endif
                });
                app.UseHangfireServer();
            }

            app.UseMvc(routes =>
            {
                routes.MapRoute(
                    name: "defaultWithArea",
                    template: "{area}/{controller=Home}/{action=Index}/{id?}");

                routes.MapRoute(
                    name: "default",
                    template: "{controller=Home}/{action=Index}/{id?}");
            });

            // Enable middleware to serve generated Swagger as a JSON endpoint
            app.UseSwagger();
            // Enable middleware to serve swagger-ui assets (HTML, JS, CSS etc.)

            app.UseSwaggerUI(options =>
            {
                options.SwaggerEndpoint("/swagger/v1/swagger.json", "API V1");
                options.IndexStream = () => Assembly.GetExecutingAssembly()
                    .GetManifestResourceStream("ARchGL.Platform.Web.wwwroot.swagger.ui.index.html");

            }); //URL: /swagger
        }
    }
}
